package models;

import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.*;

import notifiers.Mails;

import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import play.db.jpa.Model;

@Entity
@Table(name = "User_Stories")
public class UserStory extends Model {

	@Lob
	public String story;

	@Column(name = "effort_estimation")
	public double effortEstimation;

	public String priority;

	@Column(name = "creation_date")
	public Calendar creationDate;

	public String status;

	@Lob
	public String note;

	@ManyToMany(mappedBy = "userStories")
	public List<Event> events = new LinkedList<Event>();

	@ManyToMany
	public List<UserStory> dependOn = new LinkedList<UserStory>();

	@ManyToMany(mappedBy = "dependOn")
	public List<UserStory> dependants = new LinkedList<UserStory>();

	@OneToMany(mappedBy = "userStory")
	public List<Task> tasks = new LinkedList<Task>();

	@OneToMany(mappedBy = "userStory", cascade = { CascadeType.REMOVE })
	public List<DemoEntry> demos = new LinkedList<DemoEntry>();

	@ManyToMany
	@JoinTable(name = "Application_roles_User_stories", joinColumns = @JoinColumn(name = "story_id"), inverseJoinColumns = @JoinColumn(name = "appRole_id"), uniqueConstraints = { @UniqueConstraint(columnNames = {
			"story_id", "appRole_id" }) })
	public List<ApplicationRole> appRoles = new LinkedList<ApplicationRole>();

	@ManyToMany
	@JoinTable(name = "Component_User_stories", joinColumns = @JoinColumn(name = "story_id"), inverseJoinColumns = @JoinColumn(name = "component_id"), uniqueConstraints = { @UniqueConstraint(columnNames = {
			"story_id", "component_id" }) })
	public List<Component> dependOnComponents = new LinkedList<Component>();

	@ManyToOne
	public Component component;

	@ManyToOne
	public User creator;

	/**
	 * constructs a new UserStory
	 * 
	 * @param description
	 * @param effortEstimation
	 * @param priority
	 * @param creationDate
	 */
	public UserStory(String story, double effortEstimation, String priority,
			Component comp, User usr, List<UserStory> dependOn,
			List<Component> componentDep) {
		this.story = story;
		this.effortEstimation = effortEstimation;
		this.priority = priority;
		this.component = comp;
		this.creator = usr;
		this.creationDate = Calendar.getInstance();
		this.dependOn = dependOn;
		this.dependOnComponents = componentDep;
		this.status = "NEW";
	}

	/**
	 * constructs a new UserStory
	 * 
	 * @param story
	 * @param effortEstimation
	 * @param priority
	 * @param comp
	 * @param usr
	 * @param dependOn
	 * @param componentDep
	 * @param status
	 */
	public UserStory(String story, double effortEstimation, String priority,
			Component comp, User usr, List<UserStory> dependOn,
			List<Component> componentDep, String status) {
		this.story = story;
		this.status = status;
		this.effortEstimation = effortEstimation;
		this.priority = priority;
		this.component = comp;
		this.creator = usr;
		this.creationDate = Calendar.getInstance();
		this.dependOn = dependOn;
		this.dependOnComponents = componentDep;
	}

	/**
	 * constructs a UserStory
	 */
	public UserStory() {
		this.status = "NEW";
	}

	/**
	 * edits a user story. Replaces the old values with the new ones.
	 * 
	 * @param userStory
	 * @param story
	 * @param effortEstimation
	 * @param priority
	 * @param comp
	 * @param dependOn
	 * @param componentDep
	 */
	public static void editUserStory(UserStory userStory, String story,
			double effortEstimation, String priority, Component comp,
			List<UserStory> dependOn, List<Component> componentDep) {
		userStory.story = story;
		userStory.effortEstimation = effortEstimation;
		userStory.priority = priority;
		userStory.component = comp;
		userStory.creationDate = Calendar.getInstance();
		userStory.dependOn = dependOn;
		userStory.dependOnComponents = componentDep;
		userStory.save();
	}

	public static void editUserStory(UserStory userStory, String story,
			double effortEstimation, String priority, Component comp,
			List<UserStory> dependOn, List<Component> componentDep,
			List<DemoEntry> demos) {
		userStory.story = story;
		userStory.effortEstimation = effortEstimation;
		userStory.priority = priority;
		userStory.component = comp;
		userStory.creationDate = Calendar.getInstance();
		userStory.dependOn = dependOn;
		userStory.dependOnComponents = componentDep;
		userStory.demos = demos;
		userStory.save();
	}

	public static void editUserStory(UserStory userStory, String story,
			double effortEstimation, String priority, Component comp,
			List<UserStory> dependOn, List<Component> componentDep,
			List<DemoEntry> demos, List<Event> events) {
		userStory.story = story;
		userStory.effortEstimation = effortEstimation;
		userStory.priority = priority;
		userStory.component = comp;
		userStory.creationDate = Calendar.getInstance();
		userStory.dependOn = dependOn;
		userStory.dependOnComponents = componentDep;
		userStory.demos = demos;
		userStory.events = events;
		userStory.save();
	}

	public UserStory(String story, double effortEstimation, String priority,
			Component comp, User usr, List<UserStory> dependOn,
			List<Component> componentDep, List<DemoEntry> demos, String status) {
		this.story = story;
		this.effortEstimation = effortEstimation;
		this.priority = priority;
		this.component = comp;
		this.creator = usr;
		this.creationDate = Calendar.getInstance();
		this.dependOn = dependOn;
		this.dependOnComponents = componentDep;
		this.demos = demos;
		this.status = status;
	}

	public UserStory(String story, double effortEstimation, String priority,
			Component comp, User usr, List<UserStory> dependOn,
			List<Component> componentDep, List<DemoEntry> demos, String status,
			List<Event> events) {
		this.story = story;
		this.effortEstimation = effortEstimation;
		this.priority = priority;
		this.component = comp;
		this.creator = usr;
		this.creationDate = Calendar.getInstance();
		this.dependOn = dependOn;
		this.dependOnComponents = componentDep;
		this.demos = demos;
		this.status = status;
		this.events = events;
	}

	public UserStory delete() {
		while (!tasks.isEmpty()) {
			Task task = tasks.remove(0);
			this.save();
			task.delete();

		}
		for (int i = 0; i < dependants.size(); i++) {
			dependants.get(i).dependOn.remove(this);
			dependants.get(i).save();
		}
		for (int i = 0; i < events.size(); i++) {
			events.get(i).userStories.remove(this);
			events.get(i).save();
		}
		if (component.userStoriesAssigned != null) {
			component.userStoriesAssigned.remove(this);
			component.save();
		}

		return super.delete();
	}

	/**
	 * this method should be called when creating a task editing a task status
	 * it changes the related story status according to its related tasks
	 * statuses
	 * 
	 * @author Rana Ramez
	 * @param storyId
	 * @param statusId
	 * @param taskId
	 */
	public boolean checkStoryStatus(long statusId, long taskId) {

		System.out.println(statusId);
		if (this != null) {
			boolean storyDone = true;
			String message = "DONE";
			Task task = Task.findById(taskId);
			TaskStatus status = TaskStatus.findById(statusId);
			String stat = status.status;

			if (stat.equals("CLOSED")) {
				System.out.println(" Closed");
				List<Task> tasks = this.tasks;
				for (int i = 0; i < tasks.size(); i++) {
					if ((Long) tasks.get(i).status.getEntityId() != statusId) {
						storyDone = false;
						break;
					}
				}
				if (storyDone) {
					this.status = "DONE";
					this.save();
				}
			} else if (!stat.equals("NEW")) {
				if (this.status.equals("DONE") || this.status.equals("NEW")) {
					this.status = "IN PROGRESS";
					this.save();
					message = "IN PROGRESS";
				}
			} else if (stat.equals("NEW")) {
				if (this.status.equals("DONE")) {
					this.status = "IN PROGRESS";
					this.save();
					message = "IN PROGRESS";
				}
			}

			new Notification((Long) this.creator.getEntityId(),
					"Story Status Changed ", this.story
							+ " Status has been changed to " + message).save();
			Mails.notifyUserStoryStatus(this.creator, message, this);
			new Log((Long) this.getEntityId(), "this", (this.story
					+ "status changed to " + this.status),
					(Long) this.component.project.getEntityId(), "this "
							+ this.story + " status has been changed to "
							+ this.status + " because task :"
							+ task.description + " status has been changed to "
							+ stat).save();
		}
		return true;
	}

}
